#include "Plugin.h"
#include <fstream>
#include <d3dx9.h>
#include <atlbase.h>


k_Plugin::k_Plugin(IServiceLocator& ar_ServiceLocator)
	: k_Direct3D9BasePlugin(ar_ServiceLocator, L"Bloom", L"This effect will let bright spots glow.")
	, mr_ShaderSystem_(NULL)
	, mr_ShowTextureShader_(NULL)
	, mr_DefaultStateBlock_(NULL)
	, mr_ScreenMask_(NULL)
	, mr_QuarterMask_(NULL)
	, mr_BlurMask_(NULL)
	, mr_BlurToScreenMask_(NULL)
	, mb_NeedToReset(false)
{
	IEnvironment& lr_Environment = object_cast<IEnvironment>(ar_ServiceLocator);
	ms_LogFile = lr_Environment.get_ModulePath()+L"Walhall.log";

	mk_Parameters.push_back(new k_SliderParameter(*this, r_BloomGuids::BloomBias, L"BloomBias", L"BloomBias", L"The bias of the bloom.", -200, 200, 0));
	mk_Parameters.push_back(new k_SliderParameter(*this, r_BloomGuids::BloomExponent, L"BloomExponent", L"Bloom Exponent", L"The exponent of the bloom.", 1, 4000, 160));
	mk_Parameters.push_back(new k_SliderParameter(*this, r_BloomGuids::BloomBrightness, L"BloomBrightness", L"Bloom Brightness", L"The brightness of the bloom.", 0, 1000, 100));
	mk_Parameters.push_back(new k_SliderParameter(*this, r_BloomGuids::BloomSizeFactor, L"BloomSizeFactor", L"Bloom Size Factor", L"The size of the bloom texture.", 10, 50, 25));
	mk_Parameters.push_back(new k_SliderParameter(*this, r_BloomGuids::BloomBlurSteps, L"BloomBlurSteps", L"Bloom Blur Steps", L"The number of blur passes.", 0, 20, 5));

	mk_Parameters.push_back(new k_SliderParameter(*this, r_BloomGuids::BloomMotionFeedback, L"BloomMotionFeedback", L"Bloom Motion Feedback", L"The strength of the bloom motion blur.", 0, 500, 50));
	mk_Parameters.push_back(new k_SliderParameter(*this, r_BloomGuids::TonemapSaturation, L"TonemapSaturation", L"Tonemap Saturation", L"The tonemap saturation.", -200, 200, 150));
	mk_Parameters.push_back(new k_SliderParameter(*this, r_BloomGuids::TonemapContrast, L"TonemapContrast", L"Tonemap Contrast", L"The tonemap contrast.", -200, 200, 110));
	mk_Parameters.push_back(new k_SliderParameter(*this, r_BloomGuids::TonemapBrightness, L"TonemapBrightness", L"Tonemap Brightness", L"The tonemap brightness.", -200, 200, 0));

	mk_Parameters.push_back(new k_CheckBoxParameter(*this, r_BloomGuids::UseBloomMotionBlur, L"UseBloomMotionBlur", L"Use Bloom MotionBlur", L"Should the bloom use motionblur.", false));
	mk_Parameters.push_back(new k_CheckBoxParameter(*this, r_BloomGuids::UseBloom, L"UseBloom", L"Use Bloom", L"Should bloom be used.", true));
	mk_Parameters.push_back(new k_CheckBoxParameter(*this, r_BloomGuids::UseTonemapping, L"UseTonemapping", L"Use Tonemapping", L"Should tonemapping be used.", true));

	mk_Parameters.push_back(new k_CheckBoxParameter(*this, r_BloomGuids::UseCompareMode, L"UseCompareMode", L"Use Compare Mode", L"Renders only half of the desired effect.", false));
}


k_Plugin::~k_Plugin()
{
}


void k_Plugin::Initialize(IDirect3DDevice9* ar_Device_)
{
	PrintLog(ms_LogFile, L"BloomTonemap: Initialising...\n");

	mf_BloomBias = 0.0f;
	mf_BloomExponent = 8.0f;
	mf_BloomBrightness = 1.0f;
	mf_BloomMotionFeedback = 50.0f;
	mf_BloomSizeFactor = 0.25f;
	mi_BloomBlurSteps = 5;
	mb_UseBloomMotionBlur = true;
	mb_UseBloom = true;

	mf_TonemapSaturation = 1.1f;
	mf_TonemapContrast = 0.0f;
	mf_TonemapBrightness = 1.0f;
	mb_UseTonemapping = true;

	mb_UseCompareMode = false;

	ar_Device_->GetDeviceCaps(&mr_Caps);
	
	PrintLog(ms_LogFile, L"BloomTonemap: Initializing finished.\n");

	k_Direct3D9BasePlugin::Initialize(ar_Device_);
}


void k_Plugin::Deinitialize()
{
	PrintLog(ms_LogFile, L"BloomTonemap: Deinitialising...\n");
	this->set_Active(false);
	PrintLog(ms_LogFile, L"BloomTonemap: Deinitialising finished.\n");
	k_Direct3D9BasePlugin::Deinitialize();
}


void k_Plugin::Render()
{
	if (!this->CanRender())
		return;

	this->UpdateParameters();

	// set basic states
	{
		mr_CurrentStateBlock_->Capture();
		mr_DefaultStateBlock_->Apply();
	}

	// our render stuff
	{
		// reset our textures in case something on our backbuffer has changed
		this->RebuildTextures();
		// update our texture
		this->UpdateTextures();
		// render our stuff
		this->UpdateRendering();
	}

	// set states back to normal
	{
		mr_CurrentStateBlock_->Apply();
	}

	mdw_LastTime = ::GetTickCount();
}


void k_Plugin::OnBeforeReset()
{
	mb_NeedToReset = this->get_Active();
	this->set_Active(false);
}


void k_Plugin::OnAfterReset()
{
	if (mb_NeedToReset)
	{
		this->set_Active(true);
		mb_NeedToReset = false;
	}
}


void k_Plugin::set_Active(bool ab_State)
{
	if (ab_State && !this->get_Active())
	{
		PrintLog(ms_LogFile, L"BloomTonemap: Activating...\n");
		this->UpdateParameters();

		PrintLog(ms_LogFile, L"BloomTonemap: Initialising shader subsystem.\n");
		mr_ShaderSystem_ = new ShaderSystem(mr_Device_);
		mr_ShaderSystem_->AddSemanticHandler(new HelTextureSemanticHandler("BackBuffer", &mr_BackBufferTexture));
		mr_ShaderSystem_->AddSemanticHandler(new HelTextureSemanticHandler("BlurTexture", &mr_BlurTexture));
		mr_ShaderSystem_->AddSemanticHandler(new HelTextureSemanticHandler("LastBlurTexture", &mr_LastBlurTexture));

		PrintLog(ms_LogFile, L"BloomTonemap: Loading shaders...\n");
		mr_ShowTextureShader_ = this->LoadShader("ShowTexture.hlsl", "Plugins\\BloomTonemap");

		mr_ShowBlurTextureShader_ = this->LoadShader("ShowBlurTexture.hlsl", "Plugins\\BloomTonemap");
		mr_ShowLastBlurTextureShader_ = this->LoadShader("ShowLastBlurTexture.hlsl", "Plugins\\BloomTonemap");
		mr_BlurShader_ = this->LoadShader("Blur.hlsl", "Plugins\\BloomTonemap");
		mr_BloomShader_ = this->LoadShader("Bloom.hlsl", "Plugins\\BloomTonemap");
		mr_ToneBloomShader_ = this->LoadShader("ToneBloom.hlsl", "Plugins\\BloomTonemap");
		mr_BloomToScreenShader_ = this->LoadShader("BloomToScreen.hlsl", "Plugins\\BloomTonemap");
		mr_TonemapShader_ = this->LoadShader("Tonemapping.hlsl", "Plugins\\BloomTonemap");

		if (mb_UseTonemapping)
		{
			PrintLog(ms_LogFile, L"BloomTonemap: Initialising tonemap data...\n");
			mr_TonemapMatrix = this->GenerateTonemapMatrix(mf_TonemapSaturation, mf_TonemapContrast, mf_TonemapBrightness);
		}	

		PrintLog(ms_LogFile, L"BloomTonemap: Gather basic states...\n");
		mr_Device_->CreateStateBlock( D3DSBT_ALL, &mr_DefaultStateBlock_ );
		mr_Device_->CreateStateBlock( D3DSBT_ALL, &mr_CurrentStateBlock_ );
		
		mdw_LastTime = ::GetTickCount();
		PrintLog(ms_LogFile, L"BloomTonemap: Activating finished.\n");
	}
	else if (!ab_State && this->get_Active())
	{
		PrintLog(ms_LogFile, L"BloomTonemap: Deactivating...\n");

		PrintLog(ms_LogFile, L"BloomTonemap: Deinitialising mr_ShaderSystem_...\n");
		SafeDelete (mr_ShaderSystem_);
		
		PrintLog(ms_LogFile, L"BloomTonemap: Deinitialising mr_ScreenMask_...\n");
		SafeDelete (mr_ScreenMask_);

		PrintLog(ms_LogFile, L"BloomTonemap: Deinitialising mr_BlurMask_...\n");
		SafeDelete (mr_BlurMask_);

		PrintLog(ms_LogFile, L"BloomTonemap: Deinitialising mr_QuarterMask_...\n");
		SafeDelete (mr_QuarterMask_);

		PrintLog(ms_LogFile, L"BloomTonemap: Deinitialising mr_BlurToScreenMask_...\n");
		SafeDelete (mr_BlurToScreenMask_);	

		PrintLog(ms_LogFile, L"BloomTonemap: Deinitialising mr_BackBufferTexture...\n");
		if (mr_BackBufferTexture.mr_Texture_)
			mr_BackBufferTexture.mr_Texture_->Release();
		mr_BackBufferTexture.mr_Texture_ = NULL;
		mr_BackBufferTexture = r_Texture();

		PrintLog(ms_LogFile, L"BloomTonemap: Deinitialising mr_BlurTexture...\n");
		if (mr_BlurTexture.mr_Texture_)
			mr_BlurTexture.mr_Texture_->Release();
		mr_BlurTexture.mr_Texture_ = NULL;
		mr_BlurTexture = r_Texture();

		PrintLog(ms_LogFile, L"BloomTonemap: Deinitialising mr_LastBlurTexture...\n");
		if (mr_LastBlurTexture.mr_Texture_)
			mr_LastBlurTexture.mr_Texture_->Release();
		mr_LastBlurTexture.mr_Texture_ = NULL;
		mr_LastBlurTexture = r_Texture();

		PrintLog(ms_LogFile, L"BloomTonemap: Deinitialising mr_DefaultStateBlock_...\n");
		if (mr_DefaultStateBlock_)
			mr_DefaultStateBlock_->Release();
		mr_DefaultStateBlock_ = NULL;	

		PrintLog(ms_LogFile, L"BloomTonemap: Deinitialising mr_CurrentStateBlock_...\n");
		if (mr_CurrentStateBlock_)
			mr_CurrentStateBlock_->Release();
		mr_CurrentStateBlock_ = NULL;	

		PrintLog(ms_LogFile, L"BloomTonemap: Deactivating finished.\n");
	}

	k_Direct3D9BasePlugin::set_Active(ab_State);
}


bool k_Plugin::UpdateParameters()
{
	mf_BloomBias = object_cast<ISliderParameter>(this->GetParameter(r_BloomGuids::BloomBias)).get_CurrentValue()/100.0f;
	mf_BloomExponent = object_cast<ISliderParameter>(this->GetParameter(r_BloomGuids::BloomExponent)).get_CurrentValue()/100.0f;
	mf_BloomBrightness = object_cast<ISliderParameter>(this->GetParameter(r_BloomGuids::BloomBrightness)).get_CurrentValue()/100.0f;
	mf_BloomMotionFeedback = object_cast<ISliderParameter>(this->GetParameter(r_BloomGuids::BloomMotionFeedback)).get_CurrentValue()/1.0f;
	float lf_BloomSizeFactor = object_cast<ISliderParameter>(this->GetParameter(r_BloomGuids::BloomSizeFactor)).get_CurrentValue()/100.0f;
	mi_BloomBlurSteps = object_cast<ISliderParameter>(this->GetParameter(r_BloomGuids::BloomBlurSteps)).get_CurrentValue();
	float lf_TonemapSaturation = object_cast<ISliderParameter>(this->GetParameter(r_BloomGuids::TonemapSaturation)).get_CurrentValue()/100.0f;
	float lf_TonemapContrast = object_cast<ISliderParameter>(this->GetParameter(r_BloomGuids::TonemapContrast)).get_CurrentValue()/100.0f;
	float lf_TonemapBrightness = object_cast<ISliderParameter>(this->GetParameter(r_BloomGuids::TonemapBrightness)).get_CurrentValue()/100.0f;

	mb_UseTonemapping = object_cast<ICheckBoxParameter>(this->GetParameter(r_BloomGuids::UseTonemapping)).get_CurrentValue();
	mb_UseBloomMotionBlur = object_cast<ICheckBoxParameter>(this->GetParameter(r_BloomGuids::UseBloomMotionBlur)).get_CurrentValue();
	mb_UseBloom = object_cast<ICheckBoxParameter>(this->GetParameter(r_BloomGuids::UseBloom)).get_CurrentValue();

	mb_UseCompareMode = object_cast<ICheckBoxParameter>(this->GetParameter(r_BloomGuids::UseCompareMode)).get_CurrentValue();

	if (lf_BloomSizeFactor != mf_BloomSizeFactor)
	{
		if (mr_BackBufferTexture.mr_Texture_)
			mr_BackBufferTexture.mr_Texture_->Release();
		mr_BackBufferTexture.mr_Texture_ = NULL;
		mr_BackBufferTexture = r_Texture();

		if (mr_BlurTexture.mr_Texture_)
			mr_BlurTexture.mr_Texture_->Release();
		mr_BlurTexture.mr_Texture_ = NULL;
		mr_BlurTexture = r_Texture();

		if (mr_LastBlurTexture.mr_Texture_)
			mr_LastBlurTexture.mr_Texture_->Release();
		mr_LastBlurTexture.mr_Texture_ = NULL;
		mr_LastBlurTexture = r_Texture();

		mf_BloomSizeFactor = lf_BloomSizeFactor;
		this->RebuildTextures();
	}
	if (lf_TonemapSaturation != mf_TonemapSaturation
		|| lf_TonemapContrast != mf_TonemapContrast
		|| lf_TonemapBrightness != mf_TonemapBrightness)
	{
		mf_TonemapSaturation = lf_TonemapSaturation;
		mf_TonemapContrast = lf_TonemapContrast;
		mf_TonemapBrightness = lf_TonemapBrightness;
		mr_TonemapMatrix = this->GenerateTonemapMatrix(mf_TonemapSaturation, mf_TonemapContrast, mf_TonemapBrightness);
	}

	return false;
}


LPDIRECT3DTEXTURE9 k_Plugin::CreateTexture(int ai_Width, int ai_Height, D3DFORMAT ae_Format)
{
	PrintLog(ms_LogFile, L"BloomTonemap: CreateTexture: ");
	switch(ae_Format)
	{
		case D3DFMT_R8G8B8: PrintLog(ms_LogFile, L"D3DFMT_R8G8B8"); break;
		case D3DFMT_A8R8G8B8: PrintLog(ms_LogFile, L"D3DFMT_A8R8G8B8"); break;
		case D3DFMT_X8R8G8B8: PrintLog(ms_LogFile, L"D3DFMT_X8R8G8B8"); break;
		case D3DFMT_R5G6B5: PrintLog(ms_LogFile, L"D3DFMT_R5G6B5"); break;
		case D3DFMT_X1R5G5B5: PrintLog(ms_LogFile, L"D3DFMT_X1R5G5B5"); break;
		case D3DFMT_A1R5G5B5: PrintLog(ms_LogFile, L"D3DFMT_A1R5G5B5"); break;
		case D3DFMT_A4R4G4B4: PrintLog(ms_LogFile, L"D3DFMT_A4R4G4B4"); break;
		case D3DFMT_R3G3B2: PrintLog(ms_LogFile, L"D3DFMT_R3G3B2"); break;
		case D3DFMT_A8: PrintLog(ms_LogFile, L"D3DFMT_A8"); break;
		case D3DFMT_A8R3G3B2: PrintLog(ms_LogFile, L"D3DFMT_A8R3G3B2"); break;
		case D3DFMT_X4R4G4B4: PrintLog(ms_LogFile, L"D3DFMT_X4R4G4B4"); break;
		case D3DFMT_A2B10G10R10: PrintLog(ms_LogFile, L"D3DFMT_A2B10G10R10"); break;
		case D3DFMT_A8B8G8R8: PrintLog(ms_LogFile, L"D3DFMT_A8B8G8R8"); break;
		case D3DFMT_X8B8G8R8: PrintLog(ms_LogFile, L"D3DFMT_X8B8G8R8"); break;
		case D3DFMT_G16R16: PrintLog(ms_LogFile, L"D3DFMT_G16R16"); break;
		case D3DFMT_A2R10G10B10: PrintLog(ms_LogFile, L"D3DFMT_A2R10G10B10"); break;
		case D3DFMT_A16B16G16R16: PrintLog(ms_LogFile, L"D3DFMT_A16B16G16R16"); break;
		case D3DFMT_A8P8: PrintLog(ms_LogFile, L"D3DFMT_A8P8"); break;
		case D3DFMT_P8: PrintLog(ms_LogFile, L"D3DFMT_P8"); break;
		case D3DFMT_L8: PrintLog(ms_LogFile, L"D3DFMT_L8"); break;
		case D3DFMT_L16: PrintLog(ms_LogFile, L"D3DFMT_L16"); break;
		case D3DFMT_A8L8: PrintLog(ms_LogFile, L"D3DFMT_A8L8"); break;
		case D3DFMT_A4L4: PrintLog(ms_LogFile, L"D3DFMT_A4L4"); break;
		case D3DFMT_V8U8: PrintLog(ms_LogFile, L"D3DFMT_V8U8"); break;
		case D3DFMT_Q8W8V8U8: PrintLog(ms_LogFile, L"D3DFMT_Q8W8V8U8"); break;
		case D3DFMT_V16U16: PrintLog(ms_LogFile, L"D3DFMT_V16U16"); break;
		case D3DFMT_Q16W16V16U16: PrintLog(ms_LogFile, L"D3DFMT_Q16W16V16U16"); break;
		case D3DFMT_L6V5U5: PrintLog(ms_LogFile, L"D3DFMT_L6V5U5"); break;
		case D3DFMT_X8L8V8U8: PrintLog(ms_LogFile, L"D3DFMT_X8L8V8U8"); break;
		case D3DFMT_A2W10V10U10: PrintLog(ms_LogFile, L"D3DFMT_A2W10V10U10"); break;
		case D3DFMT_R16F: PrintLog(ms_LogFile, L"D3DFMT_R16F"); break;
		case D3DFMT_G16R16F: PrintLog(ms_LogFile, L"D3DFMT_G16R16F"); break;
		case D3DFMT_A16B16G16R16F: PrintLog(ms_LogFile, L"D3DFMT_A16B16G16R16F"); break;
		case D3DFMT_R32F: PrintLog(ms_LogFile, L"D3DFMT_R32F"); break;
		case D3DFMT_G32R32F: PrintLog(ms_LogFile, L"D3DFMT_G32R32F"); break;
		case D3DFMT_A32B32G32R32F: PrintLog(ms_LogFile, L"D3DFMT_A32B32G32R32F"); break;
		default: PrintLog(ms_LogFile, L"Unknown: " << ae_Format); break;
	}

	PrintLog(ms_LogFile, L" " << ai_Width << "x" << ai_Height << "\n");

	LPDIRECT3DTEXTURE9 lr_Texture_ = NULL;
	if(D3D_OK!=mr_Device_->CreateTexture(ai_Width, ai_Height, 1, D3DUSAGE_RENDERTARGET, ae_Format, D3DPOOL_DEFAULT,	&lr_Texture_,NULL))
	{
		PrintLog(ms_LogFile, L"BloomTonemap: Unable to create texture.\n");
		return NULL;
	}

	return lr_Texture_;
}


IShader* k_Plugin::LoadShader(const std::string& as_FileName, const std::string& as_Path)
{
	USES_CONVERSION;

	IEnvironment& lr_Environment = object_cast<IEnvironment>(mr_ServiceLocator);

	std::string ls_WalhallPath = W2A(lr_Environment.get_WalhallPath().c_str());
	std::string ls_File = ls_WalhallPath+as_Path+"\\"+as_FileName;
	IShader* lr_Shader_ = mr_ShaderSystem_->LoadShader(as_FileName, ls_File);
	if (!lr_Shader_)
	{
		PrintLog(ms_LogFile, L"BloomTonemap: ");
		PrintLog(ms_LogFile, A2W(ls_File.c_str()));
		PrintLog(ms_LogFile, L" shader not loaded. Critical error!\n");
	}
	else if (lr_Shader_ && !lr_Shader_->isValid())
	{
		PrintLog(ms_LogFile, L"BloomTonemap: ");
		PrintLog(ms_LogFile, A2W(ls_File.c_str()));
		PrintLog(ms_LogFile, L" shader not loaded:\n");
		PrintLog(ms_LogFile, A2W(lr_Shader_->get_ShaderErrors().c_str()));
		PrintLog(ms_LogFile, L"\n");
	}
	else
	{
		PrintLog(ms_LogFile, L"BloomTonemap: ");
		PrintLog(ms_LogFile, A2W(ls_File.c_str()));
		PrintLog(ms_LogFile, L" shader loaded.\n");
	}

	return lr_Shader_;
}


D3DXMATRIX k_Plugin::GenerateTonemapMatrix(float af_Saturation, float af_Contrast, float af_Brightness)
{
	D3DXMATRIX lr_ColorMatrix;
	::D3DXMatrixIdentity(&lr_ColorMatrix);

	// apply saturation
	const float lumR = 0.3086f;
	const float lumG = 0.6094f;
	const float lumB = 0.0820f;

	float satCompl = 1.0f - af_Saturation;
	float satComplR = lumR * satCompl;
	float satComplG = lumG * satCompl;
	float satComplB = lumB * satCompl;
	lr_ColorMatrix(0,0) = satComplR + af_Saturation;
	lr_ColorMatrix(1,0) = satComplR;
	lr_ColorMatrix(2,0) = satComplR;
	lr_ColorMatrix(0,1) = satComplG;
	lr_ColorMatrix(1,1) = satComplG + af_Saturation;
	lr_ColorMatrix(2,1) = satComplG;
	lr_ColorMatrix(0,2) = satComplB;
	lr_ColorMatrix(1,2) = satComplB;
	lr_ColorMatrix(2,2) = satComplB + af_Saturation;
	// apply brightness and contrast
	float lf_Factor = 0.5f * (1.0f - af_Contrast); 
	float lf_Bias = lf_Factor + af_Brightness; 

	for (int x=0; x<4; x++)
		for (int y=0; y<4; y++)
		lr_ColorMatrix(x,y) *= af_Contrast;

	::D3DXMatrixTranspose(&lr_ColorMatrix, &lr_ColorMatrix);

	lr_ColorMatrix(3,0) = lf_Bias;
	lr_ColorMatrix(3,1) = lf_Bias;
	lr_ColorMatrix(3,2) = lf_Bias;
	lr_ColorMatrix(3,3) = 1.0f;


	return lr_ColorMatrix;
}


void k_Plugin::CopyRenderTarget(IDirect3DTexture9* ar_Texture_, RECT* ar_Rect_)
{
	if (!ar_Texture_)
	{
		PrintLog(ms_LogFile, L"BloomTonemap: Destination texture is NULL.\n");
		return;
	}
	
	// get surfaces
	IDirect3DSurface9* lr_Dest_ = NULL;
	ar_Texture_->GetSurfaceLevel(0, &lr_Dest_);
	if (!lr_Dest_)
	{
		PrintLog(ms_LogFile, L"BloomTonemap: Unable to get surface from destination texture.\n");
	}

	IDirect3DSurface9* lr_Source_ = NULL;
	mr_Device_->GetRenderTarget(0, &lr_Source_);	
	if (!lr_Source_)
	{
		PrintLog(ms_LogFile, L"BloomTonemap: Unable to get surface from backbuffer.\n");
	}

	HRESULT lh_Result = mr_Device_->StretchRect(lr_Source_, ar_Rect_, lr_Dest_, ar_Rect_, D3DTEXF_NONE);
	if (FAILED(lh_Result))
	{
		PrintLog(ms_LogFile, L"BloomTonemap: Failed to update our textures: " << lh_Result << "\n");
	}

	lr_Dest_->Release();
	lr_Source_->Release();
}


void k_Plugin::Render(ScreenMask* ar_ScreenMask_, IShader* ar_Shader_)
{
	// update and prepare screenmask
	ar_ScreenMask_->BeginRender(mr_Device_);

	unsigned int Passes = ar_Shader_->get_PassCount();
	ar_Shader_->Begin();
	for(int CurrentPass = 0; static_cast<unsigned int>(CurrentPass) < Passes; ++CurrentPass)
	{
		RenderPassReturnValue::Enumeration ReturnValue;
		do
		{
			ReturnValue = ar_Shader_->BeginPass(CurrentPass);
			if (ReturnValue != RenderPassReturnValue::RenderAndCallNot)
			{
				ar_ScreenMask_->Render(mr_Device_);
			}
		} while(ReturnValue == RenderPassReturnValue::RenderAndCallAgain);
		ar_Shader_->EndPass(); 
	}
	ar_Shader_->End(); 
}


void k_Plugin::Blur(int ai_Steps)
{
	RECT lr_Rect;
	lr_Rect.left = lr_Rect.top = 0;
	lr_Rect.right = static_cast<LONG>(mr_QuarterMask_->width);
	lr_Rect.bottom = static_cast<LONG>(mr_QuarterMask_->height);

	// first size down, yay
	
	if (mb_UseTonemapping)
	{
		mr_TonemapShader_->get_ParameterByName("TonemapValues")->set_Matrix(mr_TonemapMatrix);
		this->Render(mr_QuarterMask_, mr_TonemapShader_);
		this->CopyRenderTarget(mr_BlurTexture.mr_Texture_, &lr_Rect);

		mr_ToneBloomShader_->get_ParameterByName("BloomValues")->set_Float4(D3DXVECTOR4(mf_BloomBias,mf_BloomExponent,mf_BloomBrightness,0));
		this->Render(mr_BlurMask_, mr_ToneBloomShader_);
	}
	else
	{
		mr_BloomShader_->get_ParameterByName("BloomValues")->set_Float4(D3DXVECTOR4(mf_BloomBias,mf_BloomExponent,mf_BloomBrightness,0));
		this->Render(mr_QuarterMask_, mr_BloomShader_);	
	}
	this->CopyRenderTarget(mr_BlurTexture.mr_Texture_, &lr_Rect);

	float ddx = 1.0f/mr_BlurTexture.mi_Width;
	float ddy = 1.0f/mr_BlurTexture.mi_Height;
	mr_BlurShader_->get_ParameterByName("sddx")->set_Float4(D3DXVECTOR4(ddx, ddy, 0, 0));
	for (int i=0; i<ai_Steps; ++i)
	{
		this->Render(mr_BlurMask_, mr_BlurShader_);
		this->CopyRenderTarget(mr_BlurTexture.mr_Texture_, &lr_Rect);
	}	

	if (mb_UseBloomMotionBlur)
	{
		float lf_Delta = (::GetTickCount()-mdw_LastTime)/1000.0f;
		float lf_Alpha = (float)std::pow(0.5f, lf_Delta/ mf_BloomMotionFeedback*500.0f);
		if (lf_Alpha>254/255.0f) lf_Alpha = 254/255.0f;
		mr_ShowLastBlurTextureShader_->get_ParameterByName("fMixStrength")->set_Float4(D3DXVECTOR4(lf_Alpha,0,0,0));
		this->Render(mr_BlurMask_, mr_ShowLastBlurTextureShader_);
		this->CopyRenderTarget(mr_LastBlurTexture.mr_Texture_, &lr_Rect);
		this->CopyRenderTarget(mr_BlurTexture.mr_Texture_, &lr_Rect);	
	}	
}


void k_Plugin::RebuildTextures()
{
	float lf_BlurTextureScale = mf_BloomSizeFactor;
	LPDIRECT3DSURFACE9 lr_RendertargetSurface_;
	D3DSURFACE_DESC lr_RenderTargetDescription;

	mr_Device_->GetRenderTarget(0, &lr_RendertargetSurface_);
	lr_RendertargetSurface_->GetDesc(&lr_RenderTargetDescription);
	lr_RendertargetSurface_->Release();

	r_Texture lr_NewBackBufferTexture;
	lr_NewBackBufferTexture.me_Format = lr_RenderTargetDescription.Format;
	lr_NewBackBufferTexture.mi_Width = lr_RenderTargetDescription.Width;
	lr_NewBackBufferTexture.mi_Height = lr_RenderTargetDescription.Height;
	lr_NewBackBufferTexture.mi_Width = lr_NewBackBufferTexture.mi_Width;
	lr_NewBackBufferTexture.mi_Height = lr_NewBackBufferTexture.mi_Height;

	if (lr_NewBackBufferTexture != mr_BackBufferTexture)
	{
		lr_NewBackBufferTexture.mr_Texture_ = this->CreateTexture(lr_NewBackBufferTexture.mi_Width, lr_NewBackBufferTexture.mi_Height, lr_NewBackBufferTexture.me_Format);
		if (mr_BackBufferTexture.mr_Texture_)
			mr_BackBufferTexture.mr_Texture_->Release();
		mr_BackBufferTexture = lr_NewBackBufferTexture;

		// rebuild screenmask
		SafeDelete (mr_ScreenMask_);
		mr_ScreenMask_ = new ScreenMask();
		mr_ScreenMask_->Create(mr_Device_, 0, 0, 1, 1);
		mr_ScreenMask_->SetTextureCoords(0,0,static_cast<float>(lr_NewBackBufferTexture.mi_Width)/lr_NewBackBufferTexture.mi_Width,static_cast<float>(lr_NewBackBufferTexture.mi_Height)/lr_NewBackBufferTexture.mi_Height);

		// rebuild blur texture
		r_Texture lr_BlurBackBufferTexture;
		lr_BlurBackBufferTexture.me_Format = lr_RenderTargetDescription.Format;
		lr_BlurBackBufferTexture.mi_Width =  static_cast<int>(mr_BackBufferTexture.mi_Width*lf_BlurTextureScale);
		lr_BlurBackBufferTexture.mi_Height = static_cast<int>(mr_BackBufferTexture.mi_Height*lf_BlurTextureScale);

		if (mr_BlurTexture.mr_Texture_)
			mr_BlurTexture.mr_Texture_->Release();
		mr_BlurTexture = lr_BlurBackBufferTexture;
		mr_BlurTexture.mr_Texture_ = this->CreateTexture(lr_BlurBackBufferTexture.mi_Width, lr_BlurBackBufferTexture.mi_Height, lr_BlurBackBufferTexture.me_Format);

		if (mr_LastBlurTexture.mr_Texture_)
			mr_LastBlurTexture.mr_Texture_->Release();
		mr_LastBlurTexture = lr_BlurBackBufferTexture;
		mr_LastBlurTexture.mr_Texture_ = this->CreateTexture(lr_BlurBackBufferTexture.mi_Width, lr_BlurBackBufferTexture.mi_Height, lr_BlurBackBufferTexture.me_Format);

		SafeDelete (mr_QuarterMask_);
		SafeDelete (mr_BlurMask_);
		SafeDelete (mr_BlurToScreenMask_);
		mr_QuarterMask_ = new ScreenMask();
		mr_QuarterMask_->Create(mr_Device_, 0, 0, static_cast<float>(lr_BlurBackBufferTexture.mi_Width), static_cast<float>(lr_BlurBackBufferTexture.mi_Height), true);
		mr_QuarterMask_->SetTextureCoords(0, 0, static_cast<float>(lr_NewBackBufferTexture.mi_Width)/lr_NewBackBufferTexture.mi_Width,static_cast<float>(lr_NewBackBufferTexture.mi_Height)/lr_NewBackBufferTexture.mi_Height);

		mr_BlurToScreenMask_ = new ScreenMask();
		mr_BlurToScreenMask_->Create(mr_Device_, 0, 0, 1, 1);
		mr_BlurToScreenMask_->SetTextureCoords(0.0f, 0.0f, 1.0f, 1.0f);

		mr_BlurMask_ = new ScreenMask();
		mr_BlurMask_->Create(mr_Device_, 0, 0, static_cast<float>(lr_BlurBackBufferTexture.mi_Width), static_cast<float>(lr_BlurBackBufferTexture.mi_Height), true);
		mr_BlurMask_->SetTextureCoords(0.0f, 0.0f, 1.0f, 1.0f);
	}	
}


void k_Plugin::UpdateTextures()
{
	RECT lr_Rect;
	lr_Rect.left = lr_Rect.top = 0;
	lr_Rect.right = mr_BackBufferTexture.mi_Width;
	lr_Rect.bottom = mr_BackBufferTexture.mi_Height;

	this->CopyRenderTarget(mr_BackBufferTexture.mr_Texture_, &lr_Rect);
}


void k_Plugin::UpdateRendering()
{
	if (mb_UseBloom)
		this->Blur(mi_BloomBlurSteps);
	if (mb_UseTonemapping)
	{
		mr_TonemapShader_->get_ParameterByName("TonemapValues")->set_Matrix(mr_TonemapMatrix);
		mr_TonemapShader_->get_ParameterByName("UseCompareMode")->set_Bool(mb_UseCompareMode);
		this->Render(mr_ScreenMask_, mr_TonemapShader_);
	}
	else
		this->Render(mr_ScreenMask_, mr_ShowTextureShader_);
	if (mb_UseBloom)
	{
		this->UpdateTextures();
		mr_BloomToScreenShader_->get_ParameterByName("UseCompareMode")->set_Bool(mb_UseCompareMode);
		this->Render(mr_BlurToScreenMask_, mr_BloomToScreenShader_);
	}
}


bool k_Plugin::CanRender()
{
	IDirect3DSurface9* lr_Surface_ = NULL;
	mr_Device_->GetRenderTarget(0, &lr_Surface_);

	IDirect3DSwapChain9* lr_SwapChain_ = NULL;
	for (unsigned int i=0; i<mr_Caps.NumberOfAdaptersInGroup; ++i)
	{
		mr_Device_->GetSwapChain(i, &lr_SwapChain_);
		if (lr_SwapChain_)
		{
			D3DPRESENT_PARAMETERS lr_Param;
			lr_SwapChain_->GetPresentParameters(&lr_Param);
			for (unsigned int j=0; j<lr_Param.BackBufferCount; ++j)
			{
				IDirect3DSurface9* lr_ScSurface_ = NULL;
				lr_SwapChain_->GetBackBuffer(j, D3DBACKBUFFER_TYPE_MONO, &lr_ScSurface_);
				if (lr_ScSurface_)
				{
					if (lr_ScSurface_==lr_Surface_)
					{
						lr_Surface_->Release();
						lr_ScSurface_->Release();
						lr_SwapChain_->Release();
						return true;
					}
					lr_ScSurface_->Release();
				}

			}
			
			lr_SwapChain_->Release();
		}
	}
	
	lr_Surface_->Release();
	return false;
}
